/**
 * 文件名: lazy_initialization_showcase_test.cpp
 *
 * 构建 & 运行（需安装 GoogleTest）：
 *   g++ -std=c++20 -pthread lazy_initialization_showcase_test.cpp \
 *       -lgtest -lgtest_main -o lazy_initialization_showcase_test
 *   ./lazy_initialization_showcase_test
 *
 * 示例结构：
 *   A. 函数内静态局部变量：线程安全懒加载 + 异常重试行为
 *   B. std::call_once：跨线程共享一次性初始化
 *   C. 双重检查锁定 (DCLP)：atomic + mutex 的安全实现
 *   D. std::mutex 包装：实现最简单的线程安全懒初始化
 *   E. 自旋锁 (atomic_flag)：短临界区场景
 *   F. 原子 shared_ptr + CAS：高并发读场景
 *
 * 每段示例都使用 GoogleTest 断言验证：
 *   - 初始化逻辑仅执行一次
 *   - 所有线程获取到的对象相同
 *   - 异常重试等边界行为符合预期
 */

#include <gtest/gtest.h>

#include <atomic>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>

// ============================================================================
// 公共辅助结构：用于统计构造次数或模拟失败。
// ============================================================================

/**
 * TrackedResource
 * --------------
 *  - value: 模拟资源内容，方便测试验证
 *  - 构造函数：递增全局计数器，用于断言初始化次数
 */
class TrackedResource {
public:
    explicit TrackedResource(int v) : value(v) {
        constructed.fetch_add(1, std::memory_order_relaxed);
    }

    int value;

    static void ResetCounter() {
        constructed.store(0, std::memory_order_relaxed);
    }

    static int ConstructedCount() {
        return constructed.load(std::memory_order_relaxed);
    }

private:
    inline static std::atomic<int> constructed{0};
};

/**
 * FailableResource
 * ----------------
 *  - 用于演示初始化时抛出异常的情况
 *  - 第一次构造故意抛出异常，第二次开始才会成功
 */
class FailableResource {
public:
    FailableResource() {
        int attempt = attempts.fetch_add(1, std::memory_order_relaxed) + 1;
        if (attempt == 1) {
            throw std::runtime_error("first attempt failed");
        }
    }

    static void ResetAttempts() {
        attempts.store(0, std::memory_order_relaxed);
    }

    static int AttemptCount() {
        return attempts.load(std::memory_order_relaxed);
    }

private:
    inline static std::atomic<int> attempts{0};
};

// ============================================================================
// A. 函数内静态局部变量
// ============================================================================

/**
 * GetSingletonViaStatic
 * ---------------------
 *  - C++11 起编译器保证函数内静态变量初始化是线程安全的
 *  - 第一次调用时构造 TrackedResource(100)
 *  - 以后直接返回已构造的对象
 */
TrackedResource& GetSingletonViaStatic() {
    static TrackedResource instance(100);
    return instance;
}

/**
 * GetFailableStatic
 * -----------------
 *  - 如果构造失败（抛异常），下一次调用会重新尝试初始化
 */
FailableResource& GetFailableStatic() {
    static FailableResource instance;
    return instance;
}

TEST(LazyInitShowcaseTest, FunctionStaticInitializesOnce) {
    TrackedResource::ResetCounter();

    constexpr int kThreads = 16;
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    TrackedResource* firstPtr = nullptr;

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([&firstPtr] {
            TrackedResource& ref = GetSingletonViaStatic();

            // 记录第一个线程看到的地址；后续线程应拿到同一个指针。
            if (firstPtr == nullptr) {
                firstPtr = &ref;
            }

            ASSERT_EQ(ref.value, 100);
            ASSERT_EQ(&ref, firstPtr);
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    EXPECT_EQ(TrackedResource::ConstructedCount(), 1);
}

TEST(LazyInitShowcaseTest, FunctionStaticRetriesAfterException) {
    FailableResource::ResetAttempts();

    // 第一次调用抛异常，初始化失败。
    EXPECT_THROW(GetFailableStatic(), std::runtime_error);
    EXPECT_EQ(FailableResource::AttemptCount(), 1);

    // 第二次调用会重新尝试并成功。
    EXPECT_NO_THROW({
        FailableResource& ref = GetFailableStatic();
        (void)ref;  // 仅用于触发初始化，避免未使用警告
    });
    EXPECT_EQ(FailableResource::AttemptCount(), 2);

    // 第三次调用应直接返回已初始化对象。
    EXPECT_NO_THROW(GetFailableStatic());
    EXPECT_EQ(FailableResource::AttemptCount(), 2);
}

// ============================================================================
// B. std::call_once + std::once_flag
// ============================================================================

namespace call_once_singleton {

std::once_flag g_flag;                           // 控制初始化只执行一次
std::unique_ptr<TrackedResource> g_instance;     // 存储资源实例
std::atomic<int> g_initCount{0};                 // 统计初始化次数

TrackedResource& GetSingleton() {
    std::call_once(g_flag, [] {
        g_instance = std::make_unique<TrackedResource>(200);
        g_initCount.fetch_add(1, std::memory_order_relaxed);
    });
    return *g_instance;
}

}  // namespace call_once_singleton

TEST(LazyInitShowcaseTest, CallOnceInitializesOnce) {
    using namespace call_once_singleton;
    g_initCount.store(0, std::memory_order_relaxed);

    constexpr int kThreads = 16;
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    TrackedResource* firstPtr = nullptr;

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([&firstPtr] {
            TrackedResource& ref = GetSingleton();
            if (firstPtr == nullptr) {
                firstPtr = &ref;
            }
            ASSERT_EQ(ref.value, 200);
            ASSERT_EQ(&ref, firstPtr);
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    EXPECT_EQ(g_initCount.load(std::memory_order_relaxed), 1);
}

// ============================================================================
// C. 双重检查锁定 (DCLP)
// ============================================================================

namespace dclp_singleton {

std::atomic<TrackedResource*> g_instance{nullptr};  // 原子指针，用于无锁读取
std::mutex g_mutex;                                 // 初始化临界区互斥锁
std::atomic<int> g_initCount{0};                    // 统计初始化次数

TrackedResource& GetSingleton() {
    // 第一次（无锁）读取，快速路径：大部分情况下直接返回指针。
    TrackedResource* ptr = g_instance.load(std::memory_order_acquire);
    if (ptr == nullptr) {
        std::lock_guard<std::mutex> lock(g_mutex);
        ptr = g_instance.load(std::memory_order_relaxed);
        if (ptr == nullptr) {
            ptr = new TrackedResource(300);
            g_initCount.fetch_add(1, std::memory_order_relaxed);
            g_instance.store(ptr, std::memory_order_release);  // 发布指针
        }
    }
    return *ptr;
}

}  // namespace dclp_singleton

TEST(LazyInitShowcaseTest, DclpInitializesOnce) {
    using namespace dclp_singleton;
    g_initCount.store(0, std::memory_order_relaxed);
    g_instance.store(nullptr, std::memory_order_relaxed);

    constexpr int kThreads = 16;
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    TrackedResource* firstPtr = nullptr;

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([&firstPtr] {
            TrackedResource& ref = GetSingleton();
            if (firstPtr == nullptr) {
                firstPtr = &ref;
            }
            ASSERT_EQ(ref.value, 300);
            ASSERT_EQ(&ref, firstPtr);
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    EXPECT_EQ(g_initCount.load(std::memory_order_relaxed), 1);
    // 注：示例未释放 new 创建的对象，真实项目需设计适当的生命周期管理。
}

// ============================================================================
// D. std::mutex 包装
// ============================================================================

/**
 * 使用 std::mutex 每次访问都串行化。虽然初始化后仍需加锁，
 * 但实现非常直接，适合访问频率不高或初始化耗时很长的场景。
 */
TrackedResource& GetMutexProtectedSingleton() {
    static std::mutex mutex;
    static std::unique_ptr<TrackedResource> instance;

    std::lock_guard<std::mutex> lock(mutex);
    if (!instance) {
        instance = std::make_unique<TrackedResource>(400);
    }
    return *instance;
}

TEST(LazyInitShowcaseTest, MutexProtectedSingleton) {
    TrackedResource::ResetCounter();

    constexpr int kThreads = 16;
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    TrackedResource* firstPtr = nullptr;

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([&firstPtr] {
            TrackedResource& ref = GetMutexProtectedSingleton();
            if (firstPtr == nullptr) {
                firstPtr = &ref;
            }
            ASSERT_EQ(ref.value, 400);
            ASSERT_EQ(&ref, firstPtr);
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    EXPECT_EQ(TrackedResource::ConstructedCount(), 1);
}

// ============================================================================
// E. 自旋锁 (atomic_flag)
// ============================================================================

/**
 * 简单自旋锁实现：适合“持锁时间极短”的初始化场景。
 * 关键点：忙等期间调用 std::this_thread::yield() 以减少 CPU 占用。
 */
class SpinLock {
public:
    void lock() {
        while (flag.test_and_set(std::memory_order_acquire)) {
            std::this_thread::yield();
        }
    }

    void unlock() {
        flag.clear(std::memory_order_release);
    }

private:
    std::atomic_flag flag = ATOMIC_FLAG_INIT;
};

TrackedResource& GetSpinLockedSingleton() {
    static SpinLock spin;
    static std::unique_ptr<TrackedResource> instance;

    std::lock_guard<SpinLock> lock(spin);
    if (!instance) {
        instance = std::make_unique<TrackedResource>(500);
    }
    return *instance;
}

TEST(LazyInitShowcaseTest, SpinLockedSingleton) {
    TrackedResource::ResetCounter();

    constexpr int kThreads = 16;
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    TrackedResource* firstPtr = nullptr;

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([&firstPtr] {
            TrackedResource& ref = GetSpinLockedSingleton();
            if (firstPtr == nullptr) {
                firstPtr = &ref;
            }
            ASSERT_EQ(ref.value, 500);
            ASSERT_EQ(&ref, firstPtr);
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    EXPECT_EQ(TrackedResource::ConstructedCount(), 1);
}

// ============================================================================
// F. 原子 shared_ptr + CAS
// ============================================================================

namespace atomic_shared_ptr_singleton {

std::atomic<std::shared_ptr<TrackedResource>> g_instance;
std::atomic<int> g_initCount{0};

std::shared_ptr<TrackedResource> GetInstance() {
    // 无锁读取：如果已有实例则直接返回
    auto current = g_instance.load(std::memory_order_acquire);
    if (!current) {
        auto newObj = std::make_shared<TrackedResource>(600);

        // compare_exchange_strong 会在失败时更新 current
        if (g_instance.compare_exchange_strong(
                current, newObj,
                std::memory_order_release,
                std::memory_order_acquire)) {
            g_initCount.fetch_add(1, std::memory_order_relaxed);
            current = std::move(newObj);
        }
        // 如果 CAS 失败，说明其他线程已经初始化成功，
        // current 会被更新为现有实例，无需额外处理。
    }
    return current;
}

}  // namespace atomic_shared_ptr_singleton

TEST(LazyInitShowcaseTest, AtomicSharedPtrSingleton) {
    using namespace atomic_shared_ptr_singleton;
    g_instance.store(nullptr, std::memory_order_relaxed);
    g_initCount.store(0, std::memory_order_relaxed);

    constexpr int kThreads = 16;
    std::vector<std::shared_ptr<TrackedResource>> results(kThreads);
    std::vector<std::thread> workers;
    workers.reserve(kThreads);

    for (int i = 0; i < kThreads; ++i) {
        workers.emplace_back([i, &results] {
            results[i] = GetInstance();
        });
    }

    for (auto& t : workers) {
        t.join();
    }

    void* firstPtr = results.front().get();
    for (const auto& ptr : results) {
        ASSERT_EQ(ptr.get(), firstPtr);
        ASSERT_EQ(ptr->value, 600);
    }

    EXPECT_EQ(g_initCount.load(std::memory_order_relaxed), 1);
    EXPECT_EQ(results.front().use_count(), kThreads + 1);  // +1 原子中保留的引用
}